﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using dbPoll_Application.Helpers;
using NHibernate;
using NHibernate.Linq;

namespace dbPoll_Application.Models
{
    public static class UserRepository
    {
        /// <summary>
        /// Adds a User to the UserRepository.
        /// </summary>
        /// <param name="user">A User to be added.</param>
        /// <returns>Returns true when the User was successfully added.</returns>
        public static Boolean Add(User user)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            using (ITransaction transaction = session.BeginTransaction())
            {
                session.Save(user);
                transaction.Commit();
                return transaction.WasCommitted;
            }
        }

        /// <summary>
        /// Updates all attributes of a User in the UserRepository.
        /// </summary>
        /// <param name="user">A User to be updated.</param>
        /// <returns>Returns true when the User was successfully updated.</returns>
        public static Boolean Update(User user)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            using (ITransaction transaction = session.BeginTransaction())
            {
                session.SaveOrUpdate(user);
                transaction.Commit();
                return transaction.WasCommitted;
            }
        }

        /// <summary>
        /// Removes a User from the UserRepository via there UserName.
        /// </summary>
        /// <param name="username">The UserName of the User to be removed.</param>
        /// <returns>Returns true when the User was successfully removed.</returns>
        public static Boolean RemoveByUserName(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            using (ITransaction transaction = session.BeginTransaction())
            {
                var user = (from item in session.Query<User>()
                            where item.UserName == username
                            select item).FirstOrDefault<User>();
                if (user != null)
                {
                    session.Delete(user);
                    transaction.Commit();
                    return true;
                }
                return false;
            }
        }

        /// <summary>
        /// Gets the total number of Users in the UserRepository.
        /// </summary>
        /// <returns>The total number of Users in the UserRepository as an int.</returns>
        public static int GetTotalUserCount()
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            int userCount = (from item in session.Query<User>()
                             select item).Count<User>();
            return userCount;
        }

        /// <summary>
        /// Gets the total number of Users in the UserRepository with a UserName like username.
        /// </summary>
        /// <param name="username">The username used to find Users.</param>
        /// <returns>The total number of Users in the UserRepository with UserName like username 
        /// as an int.</returns>
        public static int GetTotalUserCountWithUsernameLike(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            int userCount = (from item in session.Query<User>()
                             where item.UserName.Contains(username)
                             select item).Count<User>();
            return userCount;
        }

        /// <summary>
        /// Gets the total number of Users in the UserRepository with a Email like email.
        /// </summary>
        /// <param name="email">The email to find the Users.</param>
        /// <returns>The total number of Users in the UserRepository with Email like email 
        /// as an int.</returns>
        public static int GetTotalUserCountWithEmailLike(String email)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            int userCount = (from item in session.Query<User>()
                             where item.Email.Contains(email)
                             select item).Count<User>();
            return userCount;
        }

        /// <summary>
        /// Gets a collection of all of the Users in the UserRepository.
        /// </summary>
        /// <returns>A collection of all the Users in the UserRepository.</returns>
        public static IList<User> GetUsers()
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var users = from item in session.Query<User>()
                        select item;
            return users.ToList<User>();
        }

        /// <summary>
        /// Gets a collection of all of the Roles for the User with the UserName 
        /// equal to username.
        /// </summary>
        /// <returns>A collection of all the Roles of the User with the UserName 
        /// equal to username.</returns>
        public static ICollection<Role> GetRolesByUserName(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var user = (from item in session.Query<User>()
                        where item.UserName == username
                        select item).FirstOrDefault<User>();
            if (user != null)
                return user.Roles;
            return null;
        }

        public static ICollection<Role> GetRolesUserCanCreate(String UserName)
        {
            var roles = GetRolesByUserName(UserName);
            var items = new HashSet<Role>();
            foreach (Role role in roles)
            {
                items.Add(role.CanCreate);
            }
            return items;
        }

        public static IList<User> GetUsersByRoleName(String roleName)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var users = from item in session.Query<User>()
                        where item.Roles.Contains(new Role(roleName))
                        && item.IsDeleted == false
                        select item;
            return users.ToList<User>();
        }

        public static IList<User> GetPollAdministratorsInUsersGroup(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var owner = from item in session.Query<User>()
                        where item.UserName == username
                        select item;
            var users = from item in session.Query<User>()
                        where item.Roles.Contains(new Role("Poll Administrator"))
                        && item.UserName != username
                        && item.IsDeleted == false
                        && item.ClientCodes.Contains(ClientCodeRepository.GetClientCodeByUserName(username))
                        select item;
            return users.ToList<User>();
        }

        public static IList<User> GetPollAdministratorsNotInUsersGroup(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var owner = from item in session.Query<User>()
                        where item.UserName == username
                        select item;
            var users = from item in session.Query<User>()
                        where item.Roles.Contains(new Role("Poll Administrator"))
                        && item.UserName != username
                        && item.IsDeleted == false
                        && !item.ClientCodes.Contains(ClientCodeRepository.GetClientCodeByUserName(username))
                        select item;
            return users.ToList<User>();
        }

        public static IList<User> GetUsersByClientCode(ClientCode code)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var users = from item in session.Query<User>()
                        where item.ClientCodes.Contains(code)
                        select item;
            return users.ToList<User>();
        }

        /// <summary>
        /// Gets a collection of all of the Users in the UserRepository with a 
        /// UserName like username.
        /// </summary>
        /// <param name="username">The username to find the Users.</param>
        /// <returns>A collection of all the Users in the UserRepository with
        /// a UserName like username.</returns>
        public static IList<User> GetUsersWithUsernameLike(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var users = from item in session.Query<User>()
                        where item.UserName.Contains(username)
                        orderby item.UserName
                        select item;
            return users.ToList<User>();
        }

        /// <summary>
        /// Gets a collection of all of the Users in the UserRepository with a
        /// Email like email.
        /// </summary>
        /// <param name="email">The email to find the Users.</param>
        /// <returns>A collection of all the Users in the UserRepository with
        /// a Email like email.</returns>
        public static IList<User> GetUsersWithEmailLike(String email)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var users = from item in session.Query<User>()
                        where item.Email.Contains(email)
                        orderby item.UserName
                        select item;
            return users.ToList<User>();
        }

        /// <summary>
        /// Gets a User with a UserName equal to username from the UserRepository.
        /// </summary>
        /// <param name="username">The username to find the User.</param>
        /// <returns>A User with a UserName equal to username.</returns>
        public static User GetUserByUsername(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var user = (from item in session.Query<User>()
                        where item.UserName == username
                        select item).FirstOrDefault<User>();
            return user;
        }

        public static ICollection<User> GetUsersUserCanManage(String username)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var user = (from item in session.Query<User>()
                        where item.UserName == username
                        select item).FirstOrDefault<User>();

            List<User> users = new List<User>();

            if (user.Roles.Contains(RoleRepository.GetRoleByName("System Administrator")))
            {
                users = (from item in session.Query<User>()
                            where item.UserName != username
                            && item.IsDeleted == false
                            && !item.Roles.Contains(new Role("System Administrator"))
                            && item.Roles.Contains(new Role("Poll Administrator"))
                            && item.Roles.Contains(new Role("Poll Creator"))
                            && item.Roles.Contains(new Role("Poll Master"))
                            && item.Roles.Contains(new Role("Poll User"))
                            select item).ToList<User>();
            }
            else if (user.Roles.Contains(RoleRepository.GetRoleByName("Poll Administrator")))
            {
                users = (from item in session.Query<User>()
                            where item.ClientCodes.Contains(user.ClientCodes.FirstOrDefault()) 
                            && item.UserName != username
                            && item.IsDeleted == false
                            && !item.Roles.Contains(new Role("System Administrator"))
                            && !item.Roles.Contains(new Role("Poll Administrator"))
                            && (item.Roles.Contains(new Role("Poll Creator"))
                            || item.Roles.Contains(new Role("Poll Master"))
                            || item.Roles.Contains(new Role("Poll User")))
                            select item).ToList<User>();
            }
            else if (user.Roles.Contains(RoleRepository.GetRoleByName("Poll Creator")))
            {
                users = (from item in session.Query<User>()
                            where item.ClientCodes.Contains(user.ClientCodes.FirstOrDefault()) 
                            && item.UserName != username
                            && item.IsDeleted == false
                            && !item.Roles.Contains(new Role("System Administrator"))
                            && !item.Roles.Contains(new Role("Poll Administrator"))
                            && !item.Roles.Contains(new Role("Poll Creator"))
                            && (item.Roles.Contains(new Role("Poll Master"))
                            || item.Roles.Contains(new Role("Poll User")))
                            select item).ToList<User>();
            }
            else if (user.Roles.Contains(RoleRepository.GetRoleByName("Poll Master")))
            {
                users = (from item in session.Query<User>()
                            where item.ClientCodes.Contains(user.ClientCodes.FirstOrDefault()) 
                            && item.UserName != username
                            && item.IsDeleted == false
                            && !item.Roles.Contains(new Role("System Administrator"))
                            && !item.Roles.Contains(new Role("Poll Administrator"))
                            && !item.Roles.Contains(new Role("Poll Creator"))
                            && !item.Roles.Contains(new Role("Poll Master"))
                            && item.Roles.Contains(new Role("Poll User"))
                            select item).ToList<User>();
            }

            return users;
        }

        public static ICollection<User> GetUsersUserCanManage(String username, String rolename, String demographic, String organisation, String location)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var user = (from item in session.Query<User>()
                        where item.UserName == username
                        select item).FirstOrDefault<User>();

            Role systemAdministrator = RoleRepository.GetRoleByName("System Administrator");
            Role pollAdministrator = RoleRepository.GetRoleByName("Poll Administrator");
            Role pollCreator = RoleRepository.GetRoleByName("Poll Creator");
            Role pollMaster = RoleRepository.GetRoleByName("Poll Master");
            Role pollUser = RoleRepository.GetRoleByName("Poll User");
            Role selectedRole = RoleRepository.GetRoleByName(rolename);

            int roleCount = 0;
            if (rolename == "Poll User") roleCount = 1;
            if (rolename == "Poll Master") roleCount = 2;
            if (rolename == "Poll Creator") roleCount = 3;

            var users = (from item in session.Query<User>()
                     where item.UserName != username
                     && item.IsDeleted == false
                     && item.ClientCodes.Contains(user.ClientCodes.FirstOrDefault<ClientCode>())
                     select item);
            if (user.Roles.Contains(systemAdministrator))
            {
                users = users.Where(r => !r.Roles.Contains(systemAdministrator));
                users = users.Where(r => r.Roles.Contains(pollAdministrator));
                users = users.Where(r => r.Roles.Contains(pollCreator));
                users = users.Where(r => r.Roles.Contains(pollMaster));
                users = users.Where(r => r.Roles.Contains(pollUser));
            }
            else if (user.Roles.Contains(pollAdministrator))
            {
                users = users.Where(r => !r.Roles.Contains(systemAdministrator));
                users = users.Where(r => !r.Roles.Contains(pollAdministrator));
            }
            else if (user.Roles.Contains(pollCreator))
            {
                users = users.Where(r => !r.Roles.Contains(systemAdministrator));
                users = users.Where(r => !r.Roles.Contains(pollAdministrator));
                users = users.Where(r => !r.Roles.Contains(pollCreator));
            }
            else if (user.Roles.Contains(pollMaster))
            {
                users = users.Where(r => !r.Roles.Contains(systemAdministrator));
                users = users.Where(r => !r.Roles.Contains(pollAdministrator));
                users = users.Where(r => !r.Roles.Contains(pollCreator));
                users = users.Where(r => !r.Roles.Contains(pollMaster));
            }
            if (!String.IsNullOrEmpty(rolename))
            {
                users = users.Where(r => r.Roles.Contains(selectedRole) && r.Roles.Count == roleCount);
            }
            if (!String.IsNullOrEmpty(organisation))
                users = users.Where(r => r.Organisation == organisation);
            if (!String.IsNullOrEmpty(location))
                users = users.Where(r => r.City == location || r.Country == location);
            if (!String.IsNullOrEmpty(demographic))
            {
                try
                {
                    Gender gender = (Gender)Enum.Parse(typeof(Gender), demographic, true);
                    users = users.Where(r => r.Gender == gender);
                }
                catch (ArgumentException e) { users = Enumerable.Empty<User>().AsQueryable(); }
            }
            return users.ToList<User>();
        }

        public static User GetUserByUsernameAndPassword(String username, String password)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var user = (from item in session.Query<User>()
                        where item.UserName == username && item.Password == password
                        select item).FirstOrDefault<User>();
            return user;
        }

        /// <summary>
        /// Gets a User with a ProviderUserKey equal to providerUserKey from the UserRepository.
        /// </summary>
        /// <param name="providerUserKey">The providerUserKey to find the User.</param>
        /// <returns>A User with a ProviderUserKey equal to providerUserKey.</returns>
        public static User GetUserByID(object providerUserKey)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var user = (from item in session.Query<User>()
                        where item.ProviderUserKey.Equals(providerUserKey)
                        select item).FirstOrDefault<User>();
            return user;
        }

        /// <summary>
        /// Gets a User with a Email equal to email from the UserRepository.
        /// </summary>
        /// <param name="email">The email to find the User.</param>
        /// <returns>A User with a Email equal to email.</returns>
        public static User GetUserByEmail(String email)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var user = (from item in session.Query<User>()
                        where item.Email == email
                        select item).FirstOrDefault<User>();
            return user;
        }

        /// <summary>
        /// Gets the number of Users currently Online.
        /// </summary>
        /// <param name="compareTime">The DateTime used to determine the number of users
        /// online from that point onwards.</param>
        /// <returns>The number of Users currently online as an int.</returns>
        public static int GetNumberOfUsersOnline(DateTime compareTime)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            int userCount = (from item in session.Query<User>()
                             where item.LastActivityDate > compareTime
                             select item).Count<User>();
            return userCount;
        }

        public static int GetTotalUserCountWithUserNameAndRoleName(String username, String roleName)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            int userCount = (from item in session.Query<User>()
                             where item.UserName == username && item.Roles.Contains(new Role(roleName))
                             select item).Count<User>();
            return userCount;
        }

        public static IList<User> GetUsersWithUserNameLikeAndRoleName(String username, String roleName)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var users = from item in session.Query<User>()
                        where item.UserName.Contains(username) && item.Roles.Contains(new Role(roleName))
                        orderby item.UserName
                        select item;
            return users.ToList<User>();
        }

        public static IList<User> GetCreatorsOfPoll(Poll poll)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var users = (from item in session.Query<User>()
                         where item.CreatedPolls.Contains(poll)
                         select item).ToList<User>();
            return users;
        }
        
        public static List<User>GetRecipientsByPoll(Poll poll)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var recipients = (from u in session.Query<User>()
                              from p in session.Query<Participant>()
                              where u.UserName == p.linkedUser.UserName && poll.participants.Contains(p)
                              select u).ToList<User>();
            return recipients;      
        }

        public static IList<User> GetPollCreators(ClientCode code)
        {
            ISession session = NHibernateHelper.GetCurrentSession();
            var creators = from item in session.Query<User>()
                           where item.ClientCodes.Contains(code) && item.Roles.Contains(new Role("Poll Creator"))
                           select item;
            return creators.ToList<User>();
        }
    }
}
